import jsonpath
from functools import partial

import requests.exceptions
from requests.sessions import Session
from apps.interfaces.models import Api
from apps.envs.models import Env, EnvService
from core.common import (
    formatter_log,
    extract_by_jsonpath,
    replace_params_class_data,
    json_to_dict,
    list_to_dict,
    replace_all_func_value,
)
from core.check import loop_assert_by_check_list
from apps.tests.models import CaseRunLog
from core.faker import FAKER_FUNC_MAP
from core.step_model import StepModel


class ApiModel:

    def __init__(self, host, url, method, one_step_data: StepModel, params_class, case_logs_obj):
        replace_request_data = partial(replace_params_class_data, params_class, case_logs_obj)

        self.host = host
        self.url = replace_request_data(url)
        self.method = method
        self.headers = replace_request_data(list_to_dict(one_step_data.api_headers))
        self.headers = replace_all_func_value(case_logs_obj, FAKER_FUNC_MAP, self.headers)
        self.params = replace_request_data(list_to_dict(one_step_data.api_params))
        self.params = replace_all_func_value(case_logs_obj, FAKER_FUNC_MAP, self.params)
        self.extract = list_to_dict(one_step_data.extract)
        self.check = one_step_data.check
        if one_step_data.body_type == 'json':
            self.json = replace_request_data(one_step_data.api_body)
            self.json = replace_all_func_value(case_logs_obj, FAKER_FUNC_MAP, self.json)
            self.json = json_to_dict(self.json)
            self.data = {}
        else:
            self.data = replace_request_data(one_step_data.api_body)
            self.data = replace_all_func_value(case_logs_obj, FAKER_FUNC_MAP, self.data)
            self.data = json_to_dict(self.data)
            self.json = {}


def get_api_data(api_id, env_id, one_step_data, params_class, case_logs_obj):
    api = Api.objects.all().get(id=api_id)
    env_service = EnvService.objects.all().get(env=env_id, service=api.service)

    return ApiModel(host=env_service.host, url=api.url, method=api.method, one_step_data=one_step_data,
                    params_class=params_class, case_logs_obj=case_logs_obj)


def run_step_api(manager_obj, steps: list, step_key, env_id, one_step_data, params_class, case_logs_obj):

    step_index = steps.index(one_step_data)
    api_model: ApiModel = get_api_data(step_key, env_id, one_step_data, params_class, case_logs_obj)

    # 根据接口的域名判断需要几个Session
    manager_obj.add_session_manager(api_model.host)
    push_api_request_logs(case_logs_obj, api_model)
    api_response = manager_obj.session_manager[api_model.host].request(method=api_model.method,
                                                                       url=api_model.host + api_model.url,
                                                                       data=api_model.data,
                                                                       json=api_model.json,
                                                                       params=api_model.params,
                                                                       headers=api_model.headers,
                                                                       verify=False
                                                                       )
    setattr(params_class, 'status_code_' + str(step_index + 1), api_response.status_code)
    case_logs_obj.logs.append(formatter_log('info', f'【接口响应状态码: {api_response.status_code}】'))

    try:
        json_data = api_response.json()
        case_logs_obj.logs.append(formatter_log('info', f'【接口响应体: {json_data}】'))
    except requests.exceptions.JSONDecodeError:
        case_logs_obj.result = CaseRunLog.CaseResult.ERROR
        case_logs_obj.logs.append(formatter_log('error', api_response.text))
        return None

    # 提取接口的响应参数
    extract_response_set_params(json_data, api_model.extract, params_class, case_logs_obj)

    # 执行步骤的断言参数,如果步骤中有断言数据则忽略接口的断言数据
    loop_assert_by_check_list(api_model.check, params_class, case_logs_obj)


def extract_response_set_params(json_data, extract_dict_data: dict, params_class, case_logs_obj):
    """
    根据json响应体提取值并设置变量
    """
    for var_name, json_path in extract_dict_data.items():
        result, extract_data = extract_by_jsonpath(json_data, json_path)
        if result:
            setattr(params_class, var_name, extract_data)
            case_logs_obj.logs.append(formatter_log('success', f'【表达式：{json_path} 提取的值为：{extract_data}】'))
        else:
            case_logs_obj.logs.append(formatter_log('warning', '【{}】'.format(extract_data)))


def push_api_request_logs(case_logs_obj, api_mode: ApiModel):
    case_logs_obj.logs.append(formatter_log('info', f'【接口请求网址: {api_mode.host + api_mode.url}】'))
    case_logs_obj.logs.append(formatter_log('info', f'【接口请求方法: {api_mode.method}】'))
    case_logs_obj.logs.append(formatter_log('info', f'【接口请求头: {api_mode.headers}】'))
    case_logs_obj.logs.append(formatter_log('info', f'【接口请求参数: {api_mode.params}】'))
    case_logs_obj.logs.append(formatter_log('info', f'【接口请求体: {api_mode.json if api_mode.json else api_mode.data}】'))


def push_api_response_logs(case_logs_obj, status, response_body):
    case_logs_obj.logs.append(formatter_log('info', f'【接口响应状态码: {status}】'))




